package com.wujunhao1024.sqldp.workbench;


import com.wujunhao1024.sqldp.FieldTypeEnum;
import com.wujunhao1024.sqldp.entity.Field;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 工作台
 *
 * @author wujunhao
 */
public class Workbench {

    /**
     * 入口
     */
    private final String entrance;
    /**
     * 字段缓存
     */
    private final Map<String, Field> fieldMap = new HashMap<>();

    /**
     * 关联关系缓存
     */
    private final Map<String, String> parentMap = new HashMap<>();

    /**
     * Dig 层标记
     */
    private final Deque<String> digDeque = new LinkedList<>();

    /**
     * 临时区
     */
    private final Deque<List<Field>> tempFieldDeque = new LinkedList<>();

    /**
     * 临时表名 <br/>
     * 表别名 映射到 表名
     */
    private final Deque<Map<String, String>> tempTableMapDeque = new LinkedList<>();

    public Workbench() {
        entrance = digIn();
    }

    public String digIn() {
        String dig = UUID.randomUUID().toString();
        digDeque.addLast(dig);
        tempFieldDeque.addLast(new ArrayList<>());
        tempTableMapDeque.addLast(new HashMap<>());
        return dig;
    }

    public void digOut() {
        digDeque.removeLast();
        if (entrance.equals(digDeque.peekLast())) {
            return;
        }
        List<Field> outFields = tempFieldDeque.removeLast();
        List<Field> curFields = tempFieldDeque.getLast();
        for (Field cur : curFields) {
            for (Field out : outFields) {
                // 首先匹配表
                if (cur.getInnerAliasTable() != null &&
                        out.getOuterAliasTable() != null && cur.getInnerAliasTable().equals(out.getOuterAliasTable())) {
                    // 上级字段是 field 的情况
                    if (cur.getType() == 0 && cur.getFieldName() != null) {
                        if (out.getAliasName() != null && cur.getFieldName().equals(out.getAliasName())) {
                            cur.addSource(out);
                            continue;
                        }
                        if (out.getFieldName() != null && cur.getFieldName().equals(out.getFieldName())) {
                            cur.addSource(out);
                        }
                    }
                    // 上级字段是 parameter 的情况
                    if (cur.getType() == 1 && cur.getParameterName() != null) {
                        if (out.getAliasName() != null && cur.getParameterName().equals(out.getAliasName())) {
                            cur.addSource(out);
                            continue;
                        }
                        if (out.getFieldName() != null && cur.getParameterName().equals(out.getFieldName())) {
                            cur.addSource(out);
                        }
                    }
                    // 上级字段是 * 的情况
                    if (cur.getType() == 2) {
                        cur.addSource(out);
                    }
                }
            }
        }
        tempTableMapDeque.removeLast();
    }

    public String addField(String fieldName, String aliasName, String parameterName, String tableName, String innerAliasTable, String outerAliasTable) {
        Field field = new Field(fieldName, aliasName, parameterName, tableName, innerAliasTable, outerAliasTable);
        fieldMap.put(field.getUuid(), field);
        getTempFields().add(field);
        return field.getUuid();
    }

    /**
     * 通过表别名更新当前层的表名
     *
     * @param tableName      表名
     * @param tableAliasName 表别名
     */
    public void updateTable(String tableName, String tableAliasName) {
        if (tableName == null) {
            return;
        }
        getTempTableMap().put(tableAliasName, tableName);

        // 不存在表别名，默认使用表名
        if (tableAliasName == null) {
            for (Field field : getTempFields()) {
                field.setTableName(tableName);
            }
            return;
        }

        // 遍历匹配
        for (Field field : getTempFields()) {
            if (tableAliasName.equals(field.getInnerAliasTable())) {
                field.setTableName(tableName);
            }
            // 如果当前字段没有表别名，则默认赋值当前层表名
            if (field.getInnerAliasTable() == null) {
                field.setInnerAliasTable(tableAliasName);
                field.setTableName(tableName);
            }
        }
    }

    public void print() {
        for (Field field : fieldMap.values()) {
            System.out.println("FieldName: " + field.getFieldName() + ", " +
                    "AliasName: " + field.getAliasName() + ", " +
                    "ParameterName: " + field.getParameterName() + ", " +
                    "TableName: " + field.getTableName() + ", " +
                    "TableAlias: " + field.getInnerAliasTable());
        }
    }

    public List<Field> getResult() {
        List<Field> resultFields = getTempFields();

        List<Field> completed = resultFields.stream()
                .filter(field -> !FieldTypeEnum.isParameter(field.getType()))
                .collect(Collectors.toList());

        Map<String, List<Field>> needMerge = resultFields.stream()
                .filter(field -> FieldTypeEnum.isParameter(field.getType()))
                .collect(Collectors.groupingBy(Field::getFieldName));

        for (List<Field> fields : needMerge.values()) {
            Field first = fields.getFirst();
            Set<String> tableNameSet = new HashSet<>();
            Set<String> aliasTableNameSet = new HashSet<>();
            Field field = new Field(first.getFieldName(), first.getAliasName(), null, null, null);
            for (Field f : fields) {
                field.addSource(f.getSourceFields());
                tableNameSet.add(f.getTableName());
                aliasTableNameSet.add(f.getInnerAliasTable());
            }
            field.setTableName(String.join(", ", tableNameSet));
            field.setInnerAliasTable(String.join(", ", aliasTableNameSet));
            completed.add(field);
        }

        return completed;
    }

    private List<Field> getTempFields() {
        return tempFieldDeque.getLast();
    }

    private List<Field> getTempFields(String dig) {
        if (dig == null) {
            return null;
        }
        Iterator<String> digIterator = digDeque.iterator();
        Iterator<List<Field>> tempFieldIterator = tempFieldDeque.iterator();
        while (digIterator.hasNext()) {
            String cur = digIterator.next();
            if (cur.equals(dig)) {
                return tempFieldIterator.next();
            } else {
                tempFieldIterator.next();
            }
        }
        return null;
    }

    private Map<String, String> getTempTableMap(String dig) {
        if (dig == null) {
            return null;
        }
        Iterator<String> digIterator = digDeque.iterator();
        Iterator<Map<String, String>> tempTableMapIterator = tempTableMapDeque.iterator();
        while (digIterator.hasNext()) {
            String cur = digIterator.next();
            if (cur.equals(dig)) {
                return tempTableMapIterator.next();
            } else {
                tempTableMapIterator.next();
            }
        }
        return null;
    }

    private Map<String, String> getTempTableMap() {
        return tempTableMapDeque.getLast();
    }
}
